//--------------------------------------------------------------------------------------
// File: skybox.cpp
//--------------------------------------------------------------------------------------
#include "DXUT.h"
#include "DXUTcamera.h"

CFirstPersonCamera g_camera;
ID3DXMesh *g_skybox = NULL;
IDirect3DCubeTexture9 *g_skyTexture = NULL;
ID3DXEffect *g_effect = NULL;
D3DXHANDLE g_hSkyTech = 0;
D3DXHANDLE g_hWVP = 0;
D3DXHANDLE g_hViewMatrix = 0;
D3DXHANDLE g_hProjMatrix = 0;
D3DXHANDLE g_hSkyTexture = 0;

ID3DXMesh *g_teapot;
void DrawTeapot(IDirect3DDevice9 *pd3dDevice);

//--------------------------------------------------------------------------------------
// Rejects any D3D9 devices that aren't acceptable to the app by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D9DeviceAcceptable( D3DCAPS9* pCaps, D3DFORMAT AdapterFormat, D3DFORMAT BackBufferFormat,
	bool bWindowed, void* pUserContext )
{
	// Typically want to skip back buffer formats that don't support alpha blending
	IDirect3D9* pD3D = DXUTGetD3D9Object();
	if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType,
		AdapterFormat, D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING,
		D3DRTYPE_TEXTURE, BackBufferFormat ) ) )
		return false;

	return true;
}


//--------------------------------------------------------------------------------------
// Before a device is created, modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
	return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D9 resources that will live through a device reset (D3DPOOL_MANAGED)
// and aren't tied to the back buffer size
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9CreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
	void* pUserContext )
{
	D3DXCreateBox(pd3dDevice, 10.0f, 10.0f, 10.0f, &g_skybox, NULL);
	D3DXCreateCubeTextureFromFile(pd3dDevice, L"grassenvmap1024.dds", &g_skyTexture);
	D3DXCreateTeapot(pd3dDevice, &g_teapot, NULL);

	ID3DXBuffer *compilationErrors = NULL;
	D3DXCreateEffectFromFile(pd3dDevice, L"skyeffect.fx", NULL, NULL, D3DXSHADER_DEBUG, NULL, &g_effect, &compilationErrors);
	if(compilationErrors)
	{
		MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0);
		return S_FALSE;
	}
	g_hSkyTech = g_effect->GetTechniqueByName("SkyTech");
	g_hSkyTexture = g_effect->GetParameterByName(0, "gSkyTexture");
	g_hWVP = g_effect->GetParameterByName(0, "gWVP");
	g_hViewMatrix = g_effect->GetParameterByName(0, "gViewMatrix");
	g_hProjMatrix = g_effect->GetParameterByName(0, "gProjMatrix");
	
	g_camera.SetViewParams(&D3DXVECTOR3(0.0f, 0.0f, -8.0f), &D3DXVECTOR3(0.0f, 0.0f, 0.0f));
	return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D9 resources that won't live through a device reset (D3DPOOL_DEFAULT) 
// or that are tied to the back buffer size 
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D9ResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc,
	void* pUserContext )
{
	g_camera.SetProjParams(D3DX_PI / 4, pBackBufferSurfaceDesc->Width / (float)pBackBufferSurfaceDesc->Height,
		1.0f, 1000.0f);
	return S_OK;
}


//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
	g_camera.FrameMove(fElapsedTime);
}


//--------------------------------------------------------------------------------------
// Render the scene using the D3D9 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9FrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
	HRESULT hr;

	// Clear the render target and the zbuffer 
	V( pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB( 0, 45, 50, 170 ), 1.0f, 0 ) );

	// Render the scene
	if( SUCCEEDED( pd3dDevice->BeginScene() ) )
	{
		g_effect->SetTexture(g_hSkyTexture, g_skyTexture);
		g_effect->SetMatrix(g_hWVP, &((*g_camera.GetViewMatrix()) * (*g_camera.GetProjMatrix())));
		g_effect->SetMatrix(g_hViewMatrix, g_camera.GetViewMatrix());
		g_effect->SetMatrix(g_hProjMatrix, g_camera.GetProjMatrix());
		g_effect->SetTechnique(g_hSkyTech);
		g_effect->Begin(NULL, 0);
		g_effect->BeginPass(0);

		g_skybox->DrawSubset(0);

		g_effect->EndPass();
		g_effect->End();

		DrawTeapot(pd3dDevice);

		V( pd3dDevice->EndScene() );
	}
}


//--------------------------------------------------------------------------------------
// Handle messages to the application 
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
	bool* pbNoFurtherProcessing, void* pUserContext )
{
	g_camera.HandleMessages(hWnd, uMsg, wParam, lParam);
	return 0;
}


//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9ResetDevice callback 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9LostDevice( void* pUserContext )
{
}


//--------------------------------------------------------------------------------------
// Release D3D9 resources created in the OnD3D9CreateDevice callback 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D9DestroyDevice( void* pUserContext )
{
	g_effect->Release();
	g_skybox->Release();
	g_skyTexture->Release();
	g_teapot->Release();
}


//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
INT WINAPI wWinMain( HINSTANCE, HINSTANCE, LPWSTR, int )
{
	// Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

	// Set the callback functions
	DXUTSetCallbackD3D9DeviceAcceptable( IsD3D9DeviceAcceptable );
	DXUTSetCallbackD3D9DeviceCreated( OnD3D9CreateDevice );
	DXUTSetCallbackD3D9DeviceReset( OnD3D9ResetDevice );
	DXUTSetCallbackD3D9FrameRender( OnD3D9FrameRender );
	DXUTSetCallbackD3D9DeviceLost( OnD3D9LostDevice );
	DXUTSetCallbackD3D9DeviceDestroyed( OnD3D9DestroyDevice );
	DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
	DXUTSetCallbackMsgProc( MsgProc );
	DXUTSetCallbackFrameMove( OnFrameMove );

	// TODO: Perform any application-level initialization here

	// Initialize DXUT and create the desired Win32 window and Direct3D device for the application
	DXUTInit( true, true ); // Parse the command line and show msgboxes
	DXUTSetHotkeyHandling( true, true, true );  // handle the default hotkeys
	DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
	DXUTCreateWindow( L"EmptyProject" );
	DXUTCreateDevice( true, 640, 480 );

	// Start the render loop
	DXUTMainLoop();

	// TODO: Perform any application-level cleanup here

	return DXUTGetExitCode();
}

void DrawTeapot(IDirect3DDevice9 *pd3dDevice)
{
	D3DXCOLOR white(1.0f, 1.0f, 1.0f, 1.0f);
	D3DXCOLOR red(1.0f, 0.0f, 0.0f, 1.0f);
	D3DLIGHT9 light;
	light.Type      = D3DLIGHT_DIRECTIONAL;
	light.Ambient   = white * 0.6f;
	light.Diffuse   = white;
	light.Specular  = white * 0.6f;
	light.Direction = D3DXVECTOR3(1.0f, -1.0f , 1.0f);
	D3DMATERIAL9 mtrl;
	mtrl.Ambient  = red;
	mtrl.Diffuse  = red;
	mtrl.Specular = red;
	mtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 0.0f);
	mtrl.Power    = 5.0f;
	pd3dDevice->SetLight(0, &light);
	pd3dDevice->LightEnable(0, true);
	pd3dDevice->SetMaterial(&mtrl);
	pd3dDevice->SetTransform(D3DTS_VIEW, g_camera.GetViewMatrix());
	pd3dDevice->SetTransform(D3DTS_PROJECTION, g_camera.GetProjMatrix());
	g_teapot->DrawSubset(0);
}

